﻿// Deep Earth is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warranty - Use at your own risk
// View the project and the latest code at http://DeepEarth.codeplex.com/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using DeepEarth.Client.MapControl;
using DeepEarth.Client.Services.NearMap;

namespace DeepEarth.Client.Controls.DEMap
{
    [TemplatePart(Name = PART_datesList, Type = typeof(ComboBox))] 
    [TemplatePart(Name = PART_sliderCanvas, Type = typeof(Canvas))]
    public class DateLine : Control, IMapControl<Map>
    {
        private const string PART_datesList = "PART_datesList"; 
        private const string PART_sliderCanvas = "PART_sliderCanvas";

        private List<Ellipse> _marks = new List<Ellipse>();
         
        private ComboBox _datesList;
        private Canvas _sliderCanvas;

        private Map _mapInstance;
        private string _mapName;

        private DateTime? _currentDate;

        private bool _isProcessChanged = true; 

        private const double DefaultEllipsMarkHeight = 13;
        private const double DefaultEllipsMarkWidth = 13; 

        public DateLine()
        {
            DefaultStyleKey = typeof(DateLine);

            if (!DesignerProperties.IsInDesignTool)
            {
                Loaded += DateLine_Loaded;
            }
        }

        void DateLine_Loaded(object sender, RoutedEventArgs e)
        {
            SetMapInstance(MapName); 
        } 

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _datesList = (ComboBox)GetTemplateChild(PART_datesList); 
            _sliderCanvas = (Canvas)GetTemplateChild(PART_sliderCanvas);

            if (!DesignerProperties.IsInDesignTool)
            {
                if (_datesList != null)
                {
                    _datesList.SelectionChanged += DatesListSelectionChanged;

                    FillDatesList();
                    if (_datesList.Items.Count > 0)
                    {
                        _datesList.SelectedIndex = 0;
                    }
                }
            }
        }

        private void FillDatesList()
        {
            //deleting old script and generating new
            var center = MapInstance.GeoCenter; 

            if (!double.IsNaN(center.Longitude) && !double.IsNaN(center.Latitude))
            { 
                var centerInPixels = _mapInstance.CoordHelper.GeoToPixelAtZoom(center, Convert.ToInt32(_mapInstance.ZoomLevel)); 
                var wc = new WebClient();
                wc.DownloadStringCompleted += (o, e) => ParseDates(e.Result);

                var x = Convert.ToInt64(centerInPixels.X / 256);
                var y = Convert.ToInt64(centerInPixels.Y / 256);
                var z = Math.Round(_mapInstance.ZoomLevel);
                var url = string.Format("/NearMapsJsonHandler.ashx?x={0}&y={1}&z={2}", x, y, z);

                wc.DownloadStringAsync(new Uri(url, UriKind.Relative));
            }
        }

        private void DatesListSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_isProcessChanged)
            {
                if (_datesList.SelectedItem == null)
                    SetNewNearMapSource(null);
                else
                    SetNewNearMapSource((DateTime)_datesList.SelectedItem);
            }
            RefreshSlider();
        }

        private NearMapTileSource NewNearMapTileSource()
        {
            return new NearMapTileSource(NearMapModes.NearMap) { DateMap = CurrentShortDate() };
        }
         
        public void ParseDates(string data)
        {
            var jsonObject = data.JsonToObject<NearMapLayers>();
            var result = jsonObject.Layers.Vert;

            var basicDate = new DateTime(1970, 1, 1); 

            result.Reverse();

            //finding nearest date if current date is absent in dates list, then use it
            if (_datesList.SelectedItem != null)
            {
                var date = (DateTime)_datesList.SelectedItem;
                if (!result.Contains(date))
                {

                    if ((_currentDate != null) && (result.Count > 0))
                    {
                        var minDate = result[0];

                        var cmp = Math.Abs(minDate.CompareTo(date));

                        foreach (var d in result.Where(d => cmp > Math.Abs(d.CompareTo(date))))
                        {
                            minDate = d;
                        }

                        SetNewNearMapSource(minDate);
                    }
                    result.Insert(0, date);
                }

                _isProcessChanged = false;
                Dates2Marks(result);
                _datesList.ItemsSource = result;
                _datesList.SelectedItem = date;
                _isProcessChanged = true;
            }
            else
            {
                _datesList.ItemsSource = result;
                if (result.Count > 0)
                {
                    _datesList.SelectedIndex = 0;
                }
            }
        }

        private void SetNewNearMapSource(DateTime? date)
        {
            if ((_mapInstance != null) && (_currentDate != date))
            {
                var source = _mapInstance.BaseLayer.Source as NearMapTileSource;

                if (source != null)
                {
                    _mapInstance.BaseLayer.Source = NewNearMapTileSource();
                } 

                _currentDate = date;
            }
        }

        private void Events_MapViewChangedEnded(object sender, MapControl.Events.MapEventArgs args)
        {
            //recreating dates list
            FillDatesList();
        }

        private string CurrentShortDate()
        {
            if ((_mapInstance != null) && (_datesList.SelectedItem != null))
            {
                return ((DateTime)_datesList.SelectedItem).ToString("yyyyMMdd");
            }
            return null;
        }

        #region Marks

        private void Dates2Marks(IList<DateTime> datesList)
        {
            //deleting old marks
            foreach (Ellipse mark in _marks)
            {
                _sliderCanvas.Children.Remove(mark);
                mark.MouseEnter -= MarkMouseEnter;
                mark.MouseLeave -= MarkMouseLeave;
                mark.MouseLeftButtonDown -= MarkMouseLeftButtonDown;
            }

            _marks = new List<Ellipse>();

            //if one - set in centre
            if (datesList.Count == 1)
            {
                var mark = GetMark(_sliderCanvas.ActualWidth / 2, datesList[0]);

                _sliderCanvas.Children.Add(mark);
                _marks.Add(mark);

            }
            //else distributing uniformly
            else if (datesList.Count > 1)
            {
                var minDate = datesList.Min();
                var maxDate = datesList.Max();
                double duration = maxDate.Ticks - minDate.Ticks;

                foreach (var d in datesList)
                {
                    Ellipse mark = GetMark(((d.Ticks - minDate.Ticks) / duration) * _sliderCanvas.ActualWidth - DefaultEllipsMarkWidth / 2, d);
               
                    _sliderCanvas.Children.Add(mark);
                    _marks.Add(mark);
                }
            }
        }
        private Ellipse GetMark(double left, DateTime d)
        {
            var mark = new Ellipse
            {
                Height = DefaultEllipsMarkHeight,
                Width = DefaultEllipsMarkWidth,
                Stroke = new SolidColorBrush(Colors.Green),
                Fill = new SolidColorBrush(Colors.Yellow),
                DataContext = d,
                Cursor = Cursors.Hand
            };

            mark.MouseEnter += MarkMouseEnter;
            mark.MouseLeave += MarkMouseLeave;
            mark.MouseLeftButtonDown += MarkMouseLeftButtonDown;
            mark.SetValue(Canvas.TopProperty, 0.0);
            mark.SetValue(Canvas.LeftProperty, left);

            return mark;
        }

        void MarkMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _datesList.SelectedItem = (DateTime)(((Ellipse)sender).DataContext);
            RefreshSlider();
        }

        void MarkMouseLeave(object sender, MouseEventArgs e)
        {
            if ((DateTime)(((Ellipse)sender).DataContext) != (DateTime)_datesList.SelectedItem)
            {
                ((Ellipse)sender).Fill = new SolidColorBrush(Colors.Yellow);
            }
        }

        void MarkMouseEnter(object sender, MouseEventArgs e)
        {
            if ((DateTime)(((Ellipse)sender).DataContext) != (DateTime)_datesList.SelectedItem)
            {
                ((Ellipse)sender).Fill = new SolidColorBrush(Colors.Red);
            }
        }

        private void RefreshSlider()
        {
            foreach (var mark in _marks)
            {
                if ((_datesList.SelectedItem != null) && ((DateTime)(mark.DataContext) == (DateTime)_datesList.SelectedItem))
                {
                    mark.Fill = new SolidColorBrush(Colors.Green);
                }
                else
                {
                    mark.Fill = new SolidColorBrush(Colors.Yellow);
                }
            }
        }

        #endregion

        #region IMapControl<Map> Members

        public string MapName
        {
            get { return _mapName; }
            set
            {
                _mapName = value;
                SetMapInstance(MapName);
            }
        }

        public Map MapInstance
        {
            get { return _mapInstance; }
            set
            {
                if (_mapInstance != null)
                {
                    _mapInstance.Events.MapViewChangedEnded -= Events_MapViewChangedEnded;
                }
                _mapInstance = value;
                if (_mapInstance != null)
                {
                    _mapInstance.Events.MapViewChangedEnded += Events_MapViewChangedEnded;
                }
            }
        }

        public void Dispose()
        {
            MapInstance = null;
        }


        private void SetMapInstance(string mapname)
        {
            MapInstance = Utilities.FindVisualChildByName<Map>(Application.Current.RootVisual, mapname);
        }

        #endregion
       
        [DataContract]
        public class NearMapLayers
        {
            [DataMember(Name = "layers")]
            public NearMapVert Layers { get; set; }
        }

        [DataContract]
        public class NearMapVert
        {
            [DataMember(Name = "Vert")]
            public List<DateTime> Vert { get; set; }
        }
    }
}
